home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
SMTPSUBR.C
< prev
next >
Wrap
Text File
|
1993-10-14
|
16KB
|
752 lines
#include <ctype.h>
#include <conio.h>
#include <io.h>
#include "global.h"
#include "config.h"
#include "socket.h"
#include "smtp.h"
#include "domain.h"
#include "files.h"
#include "iface.h"
#include "server.h"
#ifdef MAILBOX
#include "bbs.h"
#endif
#ifdef NNTP
#include "nntp.h"
#endif
int32 Gateway = 0;
static char *Days[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
char *Months[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
char *Hdrs[] = {
"Approved: ",
"From: ",
"To: ",
"Date: ",
"Message-Id: ",
"Subject: ",
"Received: ",
"Sender: ",
"Reply-To: ",
"Status: ",
"X-BBS-Msg-Type: ",
"X-Forwarded-To: ",
"Cc: ",
"Return-Receipt-To: ",
"Apparently-To: ",
"Errors-To: ",
"Organization: ",
"Bulletin-Id: ",
"Expires: ",
"Path: ",
"Newsgroups: ",
"Distribution: ",
NULLCHAR
};
/* return the header type */
#ifdef NNTP
int
#else
static int
#endif
htype(char *s)
{
char i, *p = s;
/* check to see if there is a ':' before and white space */
while (*p != '\0' && *p != ' ' && *p != ':') {
p++;
}
if(*p != ':') {
return NOHEADER;
}
for(i = 0; Hdrs[i] != NULLCHAR; i++) {
if(strnicmp(Hdrs[i],s,strlen(Hdrs[i])) == 0) {
return i;
}
}
return UNKNOWN;
}
/* Mail routing function. For now just use the hosts file */
int32
mailroute(char *dest)
{
/*
* Look up address or use the gateway.
* Gateway must be initialized at the top of this file !!
*
*/
int32 destaddr;
if((destaddr = resolve(dest)) != 0) {
return destaddr;
}
return Gateway;
}
/* add an element to the front of the list pointed to by head
*/
struct list *
addlist(struct list **head,char *val,int type)
{
struct list *tp = mxallocw(sizeof(struct list));
tp->type = type;
tp->val = strxdup(val);
/* add entry to front of existing list */
if(*head != NULLLIST) {
tp->next = *head;
}
*head = tp;
return tp;
}
/* delete a list of list structs */
void
del_list(struct list *lp)
{
struct list *tp, *tp1 = NULLLIST;
for(tp = lp; tp != NULLLIST; tp = tp1) {
tp1 = tp->next;
xfree(tp->val);
xfree(tp);
}
}
/* Return Date/Time in Arpanet format in passed string */
char *
rfc822_date(int32 *t)
{
char *p;
static char str[40], tz[5];
/* Read the system time */
struct tm *ltm = localtime(t); /* dg1zx */
if ((p = getenv("TZ")) == NULL) {
strcpy(tz," UTC");
} else {
sprintf(tz," %.3s",p);
}
/* rfc 822 format */
sprintf(str,"%s, %02d %s %02d %02d:%02d:%02d%.4s\n",
Days[ltm->tm_wday],
ltm->tm_mday,
Months[ltm->tm_mon],
ltm->tm_year,
ltm->tm_hour,
ltm->tm_min,
ltm->tm_sec,
tz);
return str;
}
long
get_msgid(void)
{
char sfilename[MAXPATH];
long sequence = 0;
FILE *sfile;
sprintf(sfilename,"%s/sequence.seq",Mailqdir);
/* if sequence file exists, get the value, otherwise set it */
if((sfile = Fopen(sfilename,READ_TEXT,0,0)) != NULLFILE) {
/* two digits more than required */
char s[10];
fgets(s,10,sfile);
sequence = atol(s);
/* Keep it in range of and 8 digit number to use for dos name prefix. */
if(sequence < 0L || sequence > 99999999L) {
sequence = 0;
}
Fclose(sfile);
}
/* increment sequence number, and write to sequence file */
sequence++;
if((sfile = Fopen(sfilename,WRITE_TEXT,0,1)) != NULLFILE) {
fprintf(sfile,"%ld",sequence);
Fclose(sfile);
}
return sequence;
}
/* Given a string of the form <user@host>, extract the part inside the
* brackets and return a pointer to it.
*/
char *
getname(char *cp)
{
char *cp1;
if((cp = strchr(cp,'<')) == NULLCHAR) {
return NULLCHAR;
}
if((cp1 = strchr(++cp,'>')) == NULLCHAR) {
return NULLCHAR;
}
*cp1 = '\0';
return cp;
}
/* test if mail address is valid */
int
validate_address(char *s)
{
char *cp;
int perms;
/* if first character in address is a bang (!) address is NNTP_GATE
* dk5dc */
if(*s == '!') {
#ifdef NNTP
return NNTP_GATE;
#else
goto badaddr;
#endif
}
/* if address has @ in it the check dest address */
if((cp = strrchr(s,'@')) != NULLCHAR) {
int32 addr = 0;
cp++;
/* 1st check if its our hostname
* if not then check the hosts file and see
* if we can resolve ther address to a know site
* or one of our aliases
*/
if(strcmp(cp,Hostname) != 0) {
if((addr = mailroute(cp)) == 0 && Smtpmode == ROUTE) {
goto badaddr;
}
if(ismyaddr(addr) == NULLIF) {
return DOMAIN;
}
}
/* on a local address remove the host name part */
*--cp = '\0';
}
/* if using an external router leave address alone */
if(Smtpmode == QUEUE) {
return LOCAL;
}
/* check for the user%host hack */
if((cp = strrchr(s,'%')) != NULLCHAR) {
*cp++ = '@';
/* reroute based on host name following the % seperator */
if(mailroute(cp) == 0) {
goto badaddr;
} else {
return DOMAIN;
}
}
if(chkbaddoschars(s)) {
goto badaddr;
}
return LOCAL;
badaddr:
if((perms = userlogin(IPPORT_SMTP,0,s)) == 0) {
if((perms = Smtpbbs) == 0) {
perms = MAIL;
}
}
if(perms == BBS || perms == NEWS) {
return LOCAL;
}
return BADADDR;
}
#define SKIPWORD(X) while(*X && *X != ' ' && *X != '\t' && *X != '\n' && *X != ',') X++;
#define SKIPSPACE(X) while(*X == ' ' || *X == '\t' || *X == '\n' || *X == ',') X++;
/* check for an alias and expand alias into a address list */
struct list *
expandalias(struct list **head,char *user)
{
FILE *fp;
int inalias = 0;
struct list *tp = NULLLIST;
char *s, *p, buf[LINELEN];
if((fp = Fopen(Alias,READ_TEXT,0,0)) == NULLFILE) {
/* No alias file found, try to resolve M* domain name records */
if((s = resolve_mailb(user)) != NULLCHAR) {
/* remove the trailing dot */
int len = strlen(s) - 1;
if(s[len] == '.') {
s[len] = '\0';
}
/* replace first dot with @ if there is no @ */
if(strchr(s,'@') == NULLCHAR && (p = strchr(s,'.')) != NULLCHAR) {
*p = '@';
}
return addlist(head,s,(strchr(s,'@') != NULLCHAR) ? DOMAIN : LOCAL);
}
return addlist(head,user,LOCAL);
}
while(fgets(buf,LINELEN,fp) != NULLCHAR) {
p = buf;
if(*p == '#' || *p == '\0') {
continue;
}
rip(p);
/* if not in an matching entry skip continuation lines */
if(!inalias && isspace(*p)) {
continue;
}
/* when processing an active alias check for a continuation */
if(inalias) {
if(!isspace(*p)) {
break; /* done */
}
} else {
s = p;
SKIPWORD(p);
*p++ = '\0'; /* end the alias name */
if(strcmp(s,user) != 0) {
continue; /* no match go on */
}
inalias = 1;
}
/* process the recipients on the alias line */
SKIPSPACE(p);
while(*p != '\0' && *p != '#') {
char flag = LOCAL;
s = p;
SKIPWORD(p);
if(*p != '\0') {
*p++ = '\0';
}
/* find hostname */
if(strchr(s,'@') != NULLCHAR) {
flag = DOMAIN;
}
#ifdef NNTP
/* a bang overrides a DOMAIN address */
if(*s == '!') {
flag = NNTP_GATE;
}
#endif
tp = addlist(head,s,flag);
SKIPSPACE(p);
}
}
Fclose(fp);
return inalias ? tp : addlist(head,user,LOCAL);
}
/* place a mail job in the outbound queue or deliver to the appr. mailbox */
static int near
queuejob(FILE *data,char *from,struct list *to,char *host)
{
FILE *fp;
int32 msgid = get_msgid();
char fstring[MAXPATH], *Qdir = host ? Mailqdir : Routeqdir;
sprintf(fstring,"%s/%ld.txt",Qdir,msgid);
if((fp = Fopen(fstring,WRITE_TEXT,0,1)) != NULLFILE) {
char buf[LINELEN];
rewind(data);
while(fgets(buf,LINELEN,data) != NULL) {
fputs(buf,fp);
}
Fclose(fp);
sprintf(fstring,"%s/%ld.wrk",Qdir,msgid);
if((fp = Fopen(fstring,WRITE_TEXT,0,1)) != NULLFILE) {
struct list *ap;
if(host) {
fprintf(fp,"%s\n%s\n",host,from);
} else {
fprintf(fp,"From: %s\n",from);
}
for(ap = to; ap != NULLLIST; ap = ap->next) {
fprintf(fp,"%s%s\n",host ? "" : "To: ",ap->val);
log(-1,IPPORT_SMTP,"%cqueue job %ld To: %s From: %s",
host ? 'm' : 'r',msgid,ap->val,from);
}
Fclose(fp);
return 0;
}
}
quit:
Fclose(fp);
return 1;
}
/* Parse a string in the "Text: <user@host>" or "Text: user@host (Text)"
* format for the address user@host.
*/
static char * near
getaddress(char *string,int cont)
/* int cont; /* true if string is a continued header line */
{
char *cp, *ap = NULLCHAR;
int par = 0;
/* Look for <> style address */
if((cp = getname(string)) != NULLCHAR) {
return cp;
}
cp = string;
if(!cont) {
/* Skip the token */
if((cp = strchr(string,':')) == NULLCHAR) {
return NULLCHAR;
} else {
++cp;
}
}
for(; *cp != '\0'; ++cp) {
if(par && *cp == ')') {
--par;
continue;
}
/* Ignore text within parenthesis */
if(*cp == '(') {
++par;
}
if(par) {
continue;
}
if(*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == ',') {
if(ap != NULLCHAR) {
break;
}
continue;
}
if(ap == NULLCHAR) {
ap = cp;
}
}
*cp = '\0';
return ap;
}
/* Mailer daemon return mail mechanism */
int
mdaemon(
FILE *data, /* pointer to rewound data file */
char *to, /* Overridden by Errors-To: line if bounce is true */
struct list *lp, /* error log for failed mail */
int bounce) /* True for failed mail, otherwise return receipt */
{
FILE *tfile;
char buf[LINELEN], *cp, *newto = NULLCHAR;
int address_type;
if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce) {
while(fgets(buf,LINELEN,data) != NULLCHAR) {
if(*buf == '\n') {
break;
}
/* Look for Errors-To: */
if(htype(buf) == ERRORSTO && (cp = getaddress(buf,0)) != NULLCHAR) {
if(newto != NULLCHAR) {
xfree(newto);
}
newto = strxdup(cp);
break;
}
}
if(newto == NULLCHAR
&& ((to != NULLCHAR && *to == '\0') || to == NULLCHAR)) {
return -1;
}
rewind(data);
}
if((tfile = Tmpfile(0,1)) == NULLFILE) {
return -1;
}
if(newto == NULLCHAR) {
newto = strxdup(to);
}
fprintf(tfile,"%s%s%s<%ld@%s>\n",
Hdrs[DATE],
rfc822_date(&currtime),
Hdrs[MSGID],
get_msgid(),
Hostname);
fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
Hdrs[FROM],Hostname);
fprintf(tfile,"%s%s\n%s%s\n\n",
Hdrs[TO],
newto,
Hdrs[SUBJECT],
bounce ? "Failed mail" : "Return receipt");
if(bounce) {
fputs(" ==== Transcript follows ====\n\n",tfile);
for (; lp != NULLLIST; lp = lp->next) {
fprintf(tfile,"%s\n",lp->val);
}
fputs("\n ==== Unsent message follows ====\n\n",tfile);
} else {
fputs(" ==== Message header follows ====\n",tfile);
}
while(fgets(buf,LINELEN,data) != NULLCHAR) {
if(*buf == '\n') {
break;
}
fputs(buf,tfile);
}
if(bounce) {
fputc('\n',tfile);
while(fgets(buf,LINELEN,data)) {
fputs(buf,tfile);
}
}
rewind(tfile);
/* A null From<> so no looping replys to MAIL-DAEMONS */
/* check if address is ok */
if((address_type = validate_address(to)) != BADADDR) {
struct list *tolist = NULLLIST;
/* if a local address check for an alias */
if(address_type == LOCAL) {
expandalias(&tolist,newto);
} else {
/* a remote address is added to the list */
addlist(&tolist,newto,address_type);
}
mailit(tfile,"",tolist);
del_list(tolist);
}
Fclose(tfile);
xfree(newto);
return 0;
}
/* General mailit function. It takes a list of addresses which have already
* been verified and expanded for aliases. Base on the current mode the message
* is place in an mbox, the outbound smtp queue or the rqueue interface
*/
int
mailit(FILE *data,char *from,struct list *tolist)
{
struct list *ap, *dlist = NULLLIST;
char *cp, *host = NULLCHAR, *qhost, line[LINELEN], *to = NULLCHAR;
int fail = 0;
int16 perms = 0;
if(Smtpmode == QUEUE) {
return queuejob(data,from,tolist,NULLCHAR);
}
do {
qhost = NULLCHAR;
for(ap = tolist; ap != NULLLIST; ap = ap->next) {
if(ap->type == DOMAIN) {
if((host = strrchr(ap->val,'@')) != NULLCHAR) {
host++;
} else {
host = Hostname;
}
if(qhost == NULLCHAR) {
qhost = host;
}
if(stricmp(qhost,host) == 0) {
ap->type = BADADDR;
addlist(&dlist,ap->val,0);
}
}
}
if(qhost != NULLCHAR) {
rewind(data);
queuejob(data,from,dlist,qhost);
del_list(dlist);
dlist = NULLLIST;
}
} while(qhost != NULLCHAR);
for(ap = tolist; ap != NULLLIST; ap = ap->next) {
#ifdef NNTP
/*-------------------------------------------------------------------*
* dk5dc, process nntp entries !xxxx.... *
*-------------------------------------------------------------------*/
if(ap->type == NNTP_GATE) {
nnGpost(data,from,ap);
ap->type = DOMAIN;
continue;
}
#endif
if(ap->type != LOCAL) {
ap->type = DOMAIN;
continue;
}
rewind(data);
/* strip off host name of LOCAL addresses
* only if the host name is equal our Hostname
*/
if((cp = strchr(ap->val,'@')) != NULLCHAR) {
if(strstr(Hostname,cp)) {
*cp = '\0';
}
}
host = NULLCHAR;
while(fgets(line,LINELEN,data)) {
rip(line);
if(!*line) {
break;
}
if(htype(line) == TO) {
if(to != NULLCHAR) {
xfree(to);
}
to = strxdup(&line[strlen(Hdrs[TO])]);
break;
}
}
rewind(data);
if((perms = userlogin(IPPORT_SMTP,0,to)) == 0) {
if((perms = Smtpbbs) == 0) {
perms = MAIL;
}
}
xfree(to);
while(perms != 0) {
fail = 0;
rewind(data);
if(perms & NEWS) {
perms ^= NEWS;
continue;
}
if(perms & BBS) {
/* deliver to dk5sg-bbs */
fail = recv_from_mail_or_news(data,from,ap->val);
perms ^= BBS;
}
if(perms & MAIL) {
if(mlock(Mailspool,ap->val)) {
/* if mail file is busy save it in our smtp queue
* and let the smtp daemon try later.
*/
addlist(&dlist,ap->val,0);
fail = queuejob(data,Hostname,dlist,from);
del_list(dlist);
dlist = NULLLIST;
} else {
FILE *fp;
char buf[LINELEN];
int tocnt = 0;
sprintf(buf,"%s/%.8s.txt",
Mailspool,chkbaddoschars(ap->val) ? "junk" : ap->val);
if((fp = Fopen(buf,APPEND_TEXT,0,0)) != NULLFILE) {
fprintf(fp,"From %s %s",from,rfc822_date(&currtime));
host = NULLCHAR;
while(fgets(buf,LINELEN,data) != NULLCHAR) {
if(*buf == '\n') {
if(tocnt == 0) {
fprintf(fp,"%s%s\n",Hdrs[APPARTO],ap->val);
}
fputc('\n',fp);
break;
}
fputs(buf,fp);
rip(buf);
switch(htype(buf)){
case TO:
case CC:
++tocnt;
break;
case RRECEIPT:
if((cp = getaddress(buf,0)) != NULLCHAR) {
if(host != NULLCHAR) {
xfree(host);
}
host = strxdup(cp);
}
break;
}
}
while(fgets(buf,LINELEN,data)) {
fputs(buf,fp);
}
if(ferror(fp)) {
fail = 1;
} else {
fputs("\n",fp);
}
/* Leave a blank line between msgs */
Fclose(fp);
}
rmlock(Mailspool,ap->val);
}
perms ^= MAIL;
}
if(fail) {
return fail;
}
if(host != NULLCHAR) {
/* Send return receipt */
rewind(data);
mdaemon(data,host,NULLLIST,0);
xfree(host);
}
switch(Smtpquiet) {
case 0:
putch(7);
case 1:
/* timestamp by dc3sn */
tprintf("SMTP: new mail for %s from %s at %s\n",
ap->val,from,timestr(currtime));
break;
case 3:
log(-1,IPPORT_SMTP,"SMTP new mail from %s",from);
break;
}
log(-1,IPPORT_SMTP,"deliver: To: %s From: %s",ap->val,from);
}
}
return fail;
}